home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / share / arm / cli / popups.py < prev    next >
Encoding:
Python Source  |  2012-05-18  |  10.9 KB  |  338 lines

  1. """
  2. Functions for displaying popups in the interface.
  3. """
  4.  
  5. import curses
  6.  
  7. import version
  8. import cli.controller
  9.  
  10. from util import panel, uiTools
  11.  
  12. def init(height = -1, width = -1, top = 0, left = 0, belowStatic = True):
  13.   """
  14.   Preparation for displaying a popup. This creates a popup with a valid
  15.   subwindow instance. If that's successful then the curses lock is acquired
  16.   and this returns a tuple of the...
  17.   (popup, draw width, draw height)
  18.   Otherwise this leaves curses unlocked and returns None.
  19.   
  20.   Arguments:
  21.     height      - maximum height of the popup
  22.     width       - maximum width of the popup
  23.     top         - top position, relative to the sticky content
  24.     left        - left position from the screen
  25.     belowStatic - positions popup below static content if true
  26.   """
  27.   
  28.   control = cli.controller.getController()
  29.   if belowStatic:
  30.     stickyHeight = sum([stickyPanel.getHeight() for stickyPanel in control.getStickyPanels()])
  31.   else: stickyHeight = 0
  32.   
  33.   popup = panel.Panel(control.getScreen(), "popup", top + stickyHeight, left, height, width)
  34.   popup.setVisible(True)
  35.   
  36.   # Redraws the popup to prepare a subwindow instance. If none is spawned then
  37.   # the panel can't be drawn (for instance, due to not being visible).
  38.   popup.redraw(True)
  39.   if popup.win != None:
  40.     panel.CURSES_LOCK.acquire()
  41.     return (popup, popup.maxX - 1, popup.maxY)
  42.   else: return (None, 0, 0)
  43.  
  44. def finalize():
  45.   """
  46.   Cleans up after displaying a popup, releasing the cureses lock and redrawing
  47.   the rest of the display.
  48.   """
  49.   
  50.   cli.controller.getController().requestRedraw()
  51.   panel.CURSES_LOCK.release()
  52.  
  53. def inputPrompt(msg, initialValue = ""):
  54.   """
  55.   Prompts the user to enter a string on the control line (which usually
  56.   displays the page number and basic controls).
  57.   
  58.   Arguments:
  59.     msg          - message to prompt the user for input with
  60.     initialValue - initial value of the field
  61.   """
  62.   
  63.   panel.CURSES_LOCK.acquire()
  64.   control = cli.controller.getController()
  65.   msgPanel = control.getPanel("msg")
  66.   msgPanel.setMessage(msg)
  67.   msgPanel.redraw(True)
  68.   userInput = msgPanel.getstr(0, len(msg), initialValue)
  69.   control.setMsg()
  70.   panel.CURSES_LOCK.release()
  71.   return userInput
  72.  
  73. def showMsg(msg, maxWait = -1, attr = curses.A_STANDOUT):
  74.   """
  75.   Displays a single line message on the control line for a set time. Pressing
  76.   any key will end the message. This returns the key pressed.
  77.   
  78.   Arguments:
  79.     msg     - message to be displayed to the user
  80.     maxWait - time to show the message, indefinite if -1
  81.     attr    - attributes with which to draw the message
  82.   """
  83.   
  84.   panel.CURSES_LOCK.acquire()
  85.   control = cli.controller.getController()
  86.   control.setMsg(msg, attr, True)
  87.   
  88.   if maxWait == -1: curses.cbreak()
  89.   else: curses.halfdelay(maxWait * 10)
  90.   keyPress = control.getScreen().getch()
  91.   control.setMsg()
  92.   panel.CURSES_LOCK.release()
  93.   
  94.   return keyPress
  95.  
  96. def showHelpPopup():
  97.   """
  98.   Presents a popup with instructions for the current page's hotkeys. This
  99.   returns the user input used to close the popup. If the popup didn't close
  100.   properly, this is an arrow, enter, or scroll key then this returns None.
  101.   """
  102.   
  103.   popup, _, height = init(9, 80)
  104.   if not popup: return
  105.   
  106.   exitKey = None
  107.   try:
  108.     control = cli.controller.getController()
  109.     pagePanels = control.getDisplayPanels()
  110.     
  111.     # the first page is the only one with multiple panels, and it looks better
  112.     # with the log entries first, so reversing the order
  113.     pagePanels.reverse()
  114.     
  115.     helpOptions = []
  116.     for entry in pagePanels:
  117.       helpOptions += entry.getHelp()
  118.     
  119.     # test doing afterward in case of overwriting
  120.     popup.win.box()
  121.     popup.addstr(0, 0, "Page %i Commands:" % (control.getPage() + 1), curses.A_STANDOUT)
  122.     
  123.     for i in range(len(helpOptions)):
  124.       if i / 2 >= height - 2: break
  125.       
  126.       # draws entries in the form '<key>: <description>[ (<selection>)]', for
  127.       # instance...
  128.       # u: duplicate log entries (hidden)
  129.       key, description, selection = helpOptions[i]
  130.       if key: description = ": " + description
  131.       row = (i / 2) + 1
  132.       col = 2 if i % 2 == 0 else 41
  133.       
  134.       popup.addstr(row, col, key, curses.A_BOLD)
  135.       col += len(key)
  136.       popup.addstr(row, col, description)
  137.       col += len(description)
  138.       
  139.       if selection:
  140.         popup.addstr(row, col, " (")
  141.         popup.addstr(row, col + 2, selection, curses.A_BOLD)
  142.         popup.addstr(row, col + 2 + len(selection), ")")
  143.     
  144.     # tells user to press a key if the lower left is unoccupied
  145.     if len(helpOptions) < 13 and height == 9:
  146.       popup.addstr(7, 2, "Press any key...")
  147.     
  148.     popup.win.refresh()
  149.     curses.cbreak()
  150.     exitKey = control.getScreen().getch()
  151.   finally: finalize()
  152.   
  153.   if not uiTools.isSelectionKey(exitKey) and \
  154.     not uiTools.isScrollKey(exitKey) and \
  155.     not exitKey in (curses.KEY_LEFT, curses.KEY_RIGHT):
  156.     return exitKey
  157.   else: return None
  158.  
  159. def showAboutPopup():
  160.   """
  161.   Presents a popup with author and version information.
  162.   """
  163.   
  164.   popup, _, height = init(9, 80)
  165.   if not popup: return
  166.   
  167.   try:
  168.     control = cli.controller.getController()
  169.     
  170.     popup.win.box()
  171.     popup.addstr(0, 0, "About:", curses.A_STANDOUT)
  172.     popup.addstr(1, 2, "arm, version %s (released %s)" % (version.VERSION, version.LAST_MODIFIED), curses.A_BOLD)
  173.     popup.addstr(2, 4, "Written by Damian Johnson (atagar@torproject.org)")
  174.     popup.addstr(3, 4, "Project page: www.atagar.com/arm")
  175.     popup.addstr(5, 2, "Released under the GPL v3 (http://www.gnu.org/licenses/gpl.html)")
  176.     popup.addstr(7, 2, "Press any key...")
  177.     popup.win.refresh()
  178.     
  179.     curses.cbreak()
  180.     control.getScreen().getch()
  181.   finally: finalize()
  182.  
  183. def showSortDialog(title, options, oldSelection, optionColors):
  184.   """
  185.   Displays a sorting dialog of the form:
  186.   
  187.     Current Order: <previous selection>
  188.     New Order: <selections made>
  189.     
  190.     <option 1>    <option 2>    <option 3>   Cancel
  191.   
  192.   Options are colored when among the "Current Order" or "New Order", but not
  193.   when an option below them. If cancel is selected or the user presses escape
  194.   then this returns None. Otherwise, the new ordering is provided.
  195.   
  196.   Arguments:
  197.     title   - title displayed for the popup window
  198.     options      - ordered listing of option labels
  199.     oldSelection - current ordering
  200.     optionColors - mappings of options to their color
  201.   """
  202.   
  203.   popup, _, _ = init(9, 80)
  204.   if not popup: return
  205.   newSelections = []  # new ordering
  206.   
  207.   try:
  208.     cursorLoc = 0     # index of highlighted option
  209.     curses.cbreak()   # wait indefinitely for key presses (no timeout)
  210.     
  211.     selectionOptions = list(options)
  212.     selectionOptions.append("Cancel")
  213.     
  214.     while len(newSelections) < len(oldSelection):
  215.       popup.win.erase()
  216.       popup.win.box()
  217.       popup.addstr(0, 0, title, curses.A_STANDOUT)
  218.       
  219.       _drawSortSelection(popup, 1, 2, "Current Order: ", oldSelection, optionColors)
  220.       _drawSortSelection(popup, 2, 2, "New Order: ", newSelections, optionColors)
  221.       
  222.       # presents remaining options, each row having up to four options with
  223.       # spacing of nineteen cells
  224.       row, col = 4, 0
  225.       for i in range(len(selectionOptions)):
  226.         optionFormat = curses.A_STANDOUT if cursorLoc == i else curses.A_NORMAL
  227.         popup.addstr(row, col * 19 + 2, selectionOptions[i], optionFormat)
  228.         col += 1
  229.         if col == 4: row, col = row + 1, 0
  230.       
  231.       popup.win.refresh()
  232.       
  233.       key = cli.controller.getController().getScreen().getch()
  234.       if key == curses.KEY_LEFT:
  235.         cursorLoc = max(0, cursorLoc - 1)
  236.       elif key == curses.KEY_RIGHT:
  237.         cursorLoc = min(len(selectionOptions) - 1, cursorLoc + 1)
  238.       elif key == curses.KEY_UP:
  239.         cursorLoc = max(0, cursorLoc - 4)
  240.       elif key == curses.KEY_DOWN:
  241.         cursorLoc = min(len(selectionOptions) - 1, cursorLoc + 4)
  242.       elif uiTools.isSelectionKey(key):
  243.         selection = selectionOptions[cursorLoc]
  244.         
  245.         if selection == "Cancel": break
  246.         else:
  247.           newSelections.append(selection)
  248.           selectionOptions.remove(selection)
  249.           cursorLoc = min(cursorLoc, len(selectionOptions) - 1)
  250.       elif key == 27: break # esc - cancel
  251.   finally: finalize()
  252.   
  253.   if len(newSelections) == len(oldSelection):
  254.     return newSelections
  255.   else: return None
  256.  
  257. def _drawSortSelection(popup, y, x, prefix, options, optionColors):
  258.   """
  259.   Draws a series of comma separated sort selections. The whole line is bold
  260.   and sort options also have their specified color. Example:
  261.   
  262.     Current Order: Man Page Entry, Option Name, Is Default
  263.   
  264.   Arguments:
  265.     popup        - panel in which to draw sort selection
  266.     y            - vertical location
  267.     x            - horizontal location
  268.     prefix       - initial string description
  269.     options      - sort options to be shown
  270.     optionColors - mappings of options to their color
  271.   """
  272.   
  273.   popup.addstr(y, x, prefix, curses.A_BOLD)
  274.   x += len(prefix)
  275.   
  276.   for i in range(len(options)):
  277.     sortType = options[i]
  278.     sortColor = uiTools.getColor(optionColors.get(sortType, "white"))
  279.     popup.addstr(y, x, sortType, sortColor | curses.A_BOLD)
  280.     x += len(sortType)
  281.     
  282.     # comma divider between options, if this isn't the last
  283.     if i < len(options) - 1:
  284.       popup.addstr(y, x, ", ", curses.A_BOLD)
  285.       x += 2
  286.  
  287. def showMenu(title, options, oldSelection):
  288.   """
  289.   Provides menu with options laid out in a single column. User can cancel
  290.   selection with the escape key, in which case this proives -1. Otherwise this
  291.   returns the index of the selection.
  292.   
  293.   Arguments:
  294.     title        - title displayed for the popup window
  295.     options      - ordered listing of options to display
  296.     oldSelection - index of the initially selected option (uses the first
  297.                    selection without a carrot if -1)
  298.   """
  299.   
  300.   maxWidth = max(map(len, options)) + 9
  301.   popup, _, _ = init(len(options) + 2, maxWidth)
  302.   if not popup: return
  303.   key, selection = 0, oldSelection if oldSelection != -1 else 0
  304.   
  305.   try:
  306.     # hides the title of the first panel on the page
  307.     control = cli.controller.getController()
  308.     topPanel = control.getDisplayPanels(includeSticky = False)[0]
  309.     topPanel.setTitleVisible(False)
  310.     topPanel.redraw(True)
  311.     
  312.     curses.cbreak()   # wait indefinitely for key presses (no timeout)
  313.     
  314.     while not uiTools.isSelectionKey(key):
  315.       popup.win.erase()
  316.       popup.win.box()
  317.       popup.addstr(0, 0, title, curses.A_STANDOUT)
  318.       
  319.       for i in range(len(options)):
  320.         label = options[i]
  321.         format = curses.A_STANDOUT if i == selection else curses.A_NORMAL
  322.         tab = "> " if i == oldSelection else "  "
  323.         popup.addstr(i + 1, 2, tab)
  324.         popup.addstr(i + 1, 4, " %s " % label, format)
  325.       
  326.       popup.win.refresh()
  327.       
  328.       key = control.getScreen().getch()
  329.       if key == curses.KEY_UP: selection = max(0, selection - 1)
  330.       elif key == curses.KEY_DOWN: selection = min(len(options) - 1, selection + 1)
  331.       elif key == 27: selection, key = -1, curses.KEY_ENTER # esc - cancel
  332.   finally:
  333.     topPanel.setTitleVisible(True)
  334.     finalize()
  335.   
  336.   return selection
  337.  
  338.